package io.lvdaxian.mybatis12.executor.resultset;

import io.lvdaxian.mybatis12.io.Resources;
import io.lvdaxian.mybatis12.mapping.ResultMap;
import io.lvdaxian.mybatis12.session.Configuration;
import io.lvdaxian.mybatis12.type.JdbcType;
import io.lvdaxian.mybatis12.type.TypeHandler;
import io.lvdaxian.mybatis12.type.TypeHandlerRegistry;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;

/**
 * 结果集包装器
 *
 * @author lihh
 */
public class ResultSetWrapper {
  // 结果 集合
  private final ResultSet resultSet;
  // 类型事件注册器
  private final TypeHandlerRegistry typeHandlerRegistry;
  // 列名称
  private final List<String> columnNames = new ArrayList<>();
  // 类名称
  private final List<String> classNames = new ArrayList<>();
  // jdbc 类型
  private final List<JdbcType> jdbcTypes = new ArrayList<>();
  // 类型事件
  private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
  // 列 名称集合
  private Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
  private Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();
  
  public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
    super();
    // 类型事件处理
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    // 查询的结果集
    this.resultSet = rs;
    // 元数据
    final ResultSetMetaData metaData = rs.getMetaData();
    // 列 名称个数
    final int columnCount = metaData.getColumnCount();
    
    // 获取 列相关的信息
    for (int i = 1; i <= columnCount; i++) {
      // 这是列名称
      columnNames.add(metaData.getColumnLabel(i));
      // jdbc 类型
      jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
      // 类名称
      classNames.add(metaData.getColumnClassName(i));
    }
  }
  
  // 返回结果集
  public ResultSet getResultSet() {
    return resultSet;
  }
  
  // 列名称集合
  public List<String> getColumnNames() {
    return this.columnNames;
  }
  
  // 类名称 集合
  public List<String> getClassNames() {
    return Collections.unmodifiableList(classNames);
  }
  
  /**
   * 获取类型事件 根据不同的类型 拿到不同的类型处理器
   *
   * @param propertyType 属性类型
   * @param columnName   列名称
   * @return
   * @author lihh
   */
  public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName) {
    // 事件类型
    TypeHandler<?> handler = null;
    // 列 事件
    Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.get(columnName);
    // 为空的时候
    if (columnHandlers == null) {
      columnHandlers = new HashMap<>();
      typeHandlerMap.put(columnName, columnHandlers);
    } else {
      handler = columnHandlers.get(propertyType);
    }
    if (handler == null) {
      // 这里是类型事件
      handler = typeHandlerRegistry.getTypeHandler(propertyType, null);
      columnHandlers.put(propertyType, handler);
    }
    return handler;
  }
  
  private Class<?> resolveClass(String className) {
    return Resources.classForName(className);
  }
  
  /**
   * 加载 映射 or 未映射的 列名称
   *
   * @param resultMap    结果集合
   * @param columnPrefix 列前缀
   */
  private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) {
    // 被映射 and 未被映射的 文件名称
    List<String> mappedColumnNames = new ArrayList<>();
    List<String> unmappedColumnNames = new ArrayList<>();
    
    // 列的前缀
    final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
    // 根据文件前缀 映射的的列名称
    final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
    // 列 名称 遍历
    for (String columnName : columnNames) {
      // 转换大写
      final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
      if (mappedColumns.contains(upperColumnName)) {
        mappedColumnNames.add(upperColumnName);
      } else {
        unmappedColumnNames.add(columnName);
      }
    }
    mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
    unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
  }
  
  public List<String> getMappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (mappedColumnNames == null) {
      loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
      mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    }
    return mappedColumnNames;
  }
  
  public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (unMappedColumnNames == null) {
      loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
      unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    }
    return unMappedColumnNames;
  }
  
  /**
   * 这里是 id:列前缀
   *
   * @param resultMap    结果集合
   * @param columnPrefix 列前缀
   * @return
   * @author lihh
   */
  private String getMapKey(ResultMap resultMap, String columnPrefix) {
    return resultMap.getId() + ":" + columnPrefix;
  }
  
  /**
   * 该方法表示 优先考虑的前缀
   *
   * @param columnNames 列名称集合
   * @param prefix      前缀
   * @return 前缀集合
   * @author lihh
   */
  private Set<String> prependPrefixes(Set<String> columnNames, String prefix) {
    if (columnNames == null || columnNames.isEmpty() || prefix == null || prefix.isEmpty()) {
      return columnNames;
    }
    final Set<String> prefixed = new HashSet<>();
    for (String columnName : columnNames) {
      // 前缀 + 列名称
      prefixed.add(prefix + columnName);
    }
    return prefixed;
  }
  
}
